﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using Map_Creator.Painters;
using Map_Creator.XNA;
using Microsoft.Xna.Framework.Graphics;
using Point = System.Drawing.Point;
using XnaRectangle = Microsoft.Xna.Framework.Rectangle;
using XnaColor = Microsoft.Xna.Framework.Color;
using XnaPoint = Microsoft.Xna.Framework.Point;

namespace Map_Creator.MainForm.Controls
{
    public partial class MapPanel : GraphicsDeviceControl
    {
#region Public Members

        public bool IsDrawGrid;

        public event DrawModeChangedDelegate DrawModeChanged;

        private void OnDrawModeChanged()
        {
            DrawModeChangedDelegate handler = DrawModeChanged;
            if (handler != null) handler(drawMode);
        }

        private DrawMode drawMode = DrawMode.Select;

        public DrawMode DrawMode
        {
            get { return drawMode; }
            set
            {
                drawMode = value;
                OnDrawModeChanged();
            }
        }

        private readonly Dictionary<DrawMode, IPainter> painters = new Dictionary<DrawMode, IPainter>
                                                             {
                                                                 {DrawMode.Data,null},
                                                                 {DrawMode.Select,null},
                                                                 {DrawMode.Paint,null},
                                                                 {DrawMode.Object,null},
                                                             };  

#endregion

        public MapPanel()
        {
            InitializeComponent();

            MapInfo.Map.ZoomedSizeChanged += Map_ZoomedSizeChanged;
            MapInfo.Map.SelectedMapChanged += Map_SelectedMapChanged;
            MapInfo.Map.SelectedLayerChanged += Map_SelectedLayerChanged;
            MapInfo.Map.SelectedDataGridChanged += Map_SelectedDataGridChanged;
            MapInfo.Map.SelectedObjectLayerChanged += Map_SelectedObjectLayerChanged;
        }

        void Map_SelectedObjectLayerChanged(MapInfo.Map map)
        {
            if (map != MapInfo.Map.SelectedMap) return;

            if (map.CurrentObjectLayer != null)
            {
                painters[DrawMode.Object] = new ObjectPainter(map.CurrentObjectLayer);
                DrawMode = DrawMode.Object;
            }
            else
            {
                painters[DrawMode.Object] = null;
                DrawMode = DrawMode.Paint;
            }
            Invalidate();
        }

        void Map_SelectedDataGridChanged(MapInfo.Map map)
        {
            if (map != MapInfo.Map.SelectedMap) return;

            if (map.CurrentDataGrid != null)
            {
                painters[DrawMode.Data] = new DataPainter(map.CurrentDataGrid, Services);

                DrawMode = DrawMode.Data;
            }
            else
            {
                painters[DrawMode.Data] = null;
                DrawMode = DrawMode.Paint;
            }
            Invalidate();
        }

        void Map_SelectedLayerChanged(MapInfo.Map map)
        {
            // handle only if its the SelectedMap
            if (map != MapInfo.Map.SelectedMap) return;

            painters[DrawMode.Paint] = new TilePainter(MapInfo.Map.SelectedMap);
            Invalidate();

            if (DrawMode == DrawMode.Data)
            {
                //ChangeMode(MainForm.DrawMode.Paint);
                if (painters[DrawMode.Data] != null)
                {
                    // must remove current data grid... because we dont use it now :O
                    ((DataPainter) painters[DrawMode.Data]).DataGrid.AssociatedMap.CurrentDataGrid = null;
                }
            }
        }

        private void Map_SelectedMapChanged(MapInfo.Map map)
        {
            if (map != null)
            {
                var point = MathEx.ConvertToSystemPoint(map.GridSize);

                if (backBuffer != null)
                {
                    backBuffer.Dispose();
                    backBuffer = null;
                }
                backBuffer = new RenderTarget2D(GraphicsDevice,point.X * map.TileSize, point.Y * map.TileSize,false,SurfaceFormat.Color,DepthFormat.Depth24,0,RenderTargetUsage.PreserveContents);
                painters[DrawMode.Select] = map.Selection;
            }
            else
            {
                if (backBuffer != null)
                {
                    backBuffer.Dispose();
                    backBuffer = null;
                }
            }

            Map_ZoomedSizeChanged(map);
        }

        private void Map_ZoomedSizeChanged(MapInfo.Map map)
        {
            if (map != null)
            {
                Size = new Size(
                    (map.GridSize.X*map.ZoomedTileSize),
                    (map.GridSize.Y*map.ZoomedTileSize));
            }
            else
            {
                Size = Size.Empty;
            }

            Invalidate();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            var painter = painters[DrawMode];
            if (painter != null)
            {
                var position = e.Location;
                position.X = MathEx.Clamp(position.X, 0, Width - 1);
                position.Y = MathEx.Clamp(position.Y, 0, Height - 1);

                var tilePosition = new XnaPoint(position.X / MapInfo.Map.SelectedMap.ZoomedTileSize, position.Y / MapInfo.Map.SelectedMap.ZoomedTileSize);

                var invalidationRectangle = painter.OnMouseMove(tilePosition, e.Button);

                if (invalidationRectangle != XnaRectangle.Empty)
                    Invalidate(MathEx.ConvertToSystemRect(MapInfo.Map.SelectedMap.GetScreenSize(invalidationRectangle)));
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Middle) MapInfo.Map.SelectedMap.ChangeZoom(MapInfo.Map.SelectedMap.TileSize - MapInfo.Map.SelectedMap.ZoomedTileSize);

            var painter = painters[DrawMode];
            if (painter != null)
            {
                var position = e.Location;
                position.X = MathEx.Clamp(position.X, 0, Width - 1);
                position.Y = MathEx.Clamp(position.Y, 0, Height - 1);

                var tilePosition = new XnaPoint(position.X / MapInfo.Map.SelectedMap.ZoomedTileSize, position.Y / MapInfo.Map.SelectedMap.ZoomedTileSize);

                var invalidationRectangle = painter.OnMouseDown(tilePosition, e.Button);
                if (invalidationRectangle != new XnaRectangle())
                    Invalidate(MathEx.ConvertToSystemRect(MapInfo.Map.SelectedMap.GetScreenSize(invalidationRectangle)));
            }
        }

        protected override void Draw(PaintEventArgs e)
        {
            if (MapInfo.Map.SelectedMap == null) return;
            if (MapInfo.Map.SelectedMap.Sets == 0) return;

            xnaGraphics.Begin();

            var clip = e.ClipRectangle;

            // calculate tile rectangle
            var left = clip.Left / MapInfo.Map.SelectedMap.ZoomedTileSize;
            var top = clip.Top / MapInfo.Map.SelectedMap.ZoomedTileSize;
            var right = (int)Math.Ceiling(clip.Right / (float)(MapInfo.Map.SelectedMap.ZoomedTileSize));
            var bottom = (int)Math.Ceiling(clip.Bottom / (float)(MapInfo.Map.SelectedMap.ZoomedTileSize));


            var tilesRectangle = new XnaRectangle(left, top, right - left, bottom - top);
            var tileSize = MapInfo.Map.SelectedMap.TileSize;
            var realClipRect = new XnaRectangle(left * tileSize, top * tileSize, (right - left) * tileSize, (bottom - top) * tileSize);
            // draw background

            xnaGraphics.FillRectangle(realClipRect, XnaColor.FromNonPremultiplied(BackColor.R, BackColor.G, BackColor.B, BackColor.A));

            DrawTiles(new Point(left, top), new Point(right, bottom));
            

            if (DrawMode == DrawMode.Select)
                MapInfo.Map.SelectedMap.Selection.OnDraw(xnaGraphics, tilesRectangle);

            // Draw Grid
            DrawGrid(new Point(left, top), new Point(right, bottom));

            var painter = painters[DrawMode];
            if (painter != null)
            {
                painter.OnDraw(xnaGraphics, tilesRectangle);
            }

            xnaGraphics.End();
        }

        private void DrawGrid(Point topLeft, Point buttomRight)
        {
            if (IsDrawGrid)
            {
                for (int i = topLeft.X; i < buttomRight.X && i < MapInfo.Map.SelectedMap.GridSize.X; i++)
                {
                    for (int j = topLeft.Y; j < buttomRight.Y && j < MapInfo.Map.SelectedMap.GridSize.Y; j++)
                    {
                        xnaGraphics.DrawRectangle(new XnaRectangle(i*MapInfo.Map.SelectedMap.TileSize, j*MapInfo.Map.SelectedMap.TileSize,
                                                             MapInfo.Map.SelectedMap.TileSize-1, MapInfo.Map.SelectedMap.TileSize-1),XnaColor.Multiply(XnaColor.White,.4f));
                    }
                }
            }
        }

        private void DrawTiles(Point topLeft, Point buttomRight)
        {
            var tileSize = MapInfo.Map.SelectedMap.TileSize;
            for (int l = 0; l < MapInfo.Map.SelectedMap.Levels; l++)
            {
                var alpha = 1f;
                if (l > MapInfo.Map.SelectedMap.CurrentLayer.Level)
                {
                    alpha = (MapInfo.Map.SelectedMap.Levels - l + MapInfo.Map.SelectedMap.CurrentLayer.Level) / (float)MapInfo.Map.SelectedMap.Levels;
                }
                alpha *= (MapInfo.Map.SelectedMap[l].opacity / 100f);
                var color = XnaColor.Multiply(XnaColor.White, alpha);
                for (int i = topLeft.X; i < buttomRight.X && i < MapInfo.Map.SelectedMap.GridSize.X; i++)
                {
                    for (int j = topLeft.Y; j < buttomRight.Y && j < MapInfo.Map.SelectedMap.GridSize.Y; j++)
                    {
                        var tile = MapInfo.Map.SelectedMap[l][i, j];
                        if (tile == null) continue;

                        tile.DrawTile(xnaGraphics, MapInfo.Map.SelectedMap,
                                      new XnaRectangle(i*tileSize, j*tileSize, tileSize, tileSize),
                                      color);
                    }
                }
            }
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            Parent.Focus();
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            var painter = painters[DrawMode];
            if (painter != null)
            {
                var invalidationRectangle = painter.OnMouseLeave();
                if (invalidationRectangle != new XnaRectangle())
                    Invalidate(MathEx.ConvertToSystemRect(MapInfo.Map.SelectedMap.GetScreenSize(invalidationRectangle)));
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            var painter = painters[DrawMode];
            if (painter != null)
            {
                var invalidationRectangle = painter.OnMouseUp();
                if (invalidationRectangle != new XnaRectangle())
                    Invalidate(MathEx.ConvertToSystemRect(MapInfo.Map.SelectedMap.GetScreenSize(invalidationRectangle)));
            }
        }

        private int minDelta = 1000;

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            var absDelta = Math.Abs(e.Delta);
            if (absDelta < minDelta && absDelta != 0)
                minDelta = absDelta;
            if (e.Delta != 0)
                MapInfo.Map.SelectedMap.ChangeZoom(e.Delta / minDelta);
        }
    }
}